home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 1992 August / info-mac-1992.iso / Applications (app) / STvi / stevie 3.10 / normal.c < prev    next >
Text File  |  1991-01-03  |  17KB  |  988 lines

  1. /* $Header: /nw/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $
  2.  *
  3.  * Contains the main routine for processing characters in command mode.
  4.  * Communicates closely with the code in ops.c to handle the operators.
  5.  */
  6.  
  7. #include "stevie.h"
  8. #include "ops.h"
  9.  
  10. /*
  11.  * Generally speaking, every command in normal() should either clear any
  12.  * pending operator (with CLEAROP), or set the motion type variable.
  13.  */
  14.  
  15. #define    CLEAROP    (operator=NOP)    /* clear any pending operator */
  16.  
  17. int    operator = NOP;        /* current pending operator */
  18. int    mtype;            /* type of the current cursor motion */
  19. bool_t    mincl;            /* true if char motion is inclusive */
  20. LPTR    startop;        /* cursor pos. at start of operator */
  21.  
  22. /*
  23.  * Operators can have counts either before the operator, or between the
  24.  * operator and the following cursor motion as in:
  25.  *
  26.  *    d3w or 3dw
  27.  *
  28.  * If a count is given before the operator, it is saved in opnum. If
  29.  * normal() is called with a pending operator, the count in opnum (if
  30.  * present) overrides any count that came later.
  31.  */
  32. static    int    opnum = 0;
  33.  
  34. #define    DEFAULT1(x)    (((x) == 0) ? 1 : (x))
  35.  
  36. /*
  37.  * normal(c)
  38.  *
  39.  * Execute a command in command mode.
  40.  *
  41.  * This is basically a big switch with the cases arranged in rough categories
  42.  * in the following order:
  43.  *
  44.  *    1. File positioning commands
  45.  *    2. Control commands (e.g. ^G, Z, screen redraw, etc)
  46.  *    3. Character motions
  47.  *    4. Search commands (of various kinds)
  48.  *    5. Edit commands (e.g. J, x, X)
  49.  *    6. Insert commands (e.g. i, o, O, A)
  50.  *    7. Operators
  51.  *    8. Abbreviations (e.g. D, C)
  52.  *    9. Marks
  53.  */
  54. void
  55. normal(c)
  56. register int    c;
  57. {
  58.     register int    n;
  59.     register char    *s;    /* temporary variable for misc. strings */
  60.     bool_t    flag = FALSE;
  61.     int    type = 0;    /* used in some operations to modify type */
  62.     int    dir = FORWARD;    /* search direction */
  63.     int    nchar = NUL;
  64.     bool_t    finish_op;
  65.  
  66.     /*
  67.      * If there is an operator pending, then the command we take
  68.      * this time will terminate it. Finish_op tells us to finish
  69.      * the operation before returning this time (unless the operation
  70.      * was cancelled.
  71.      */
  72.     finish_op = (operator != NOP);
  73.  
  74.     /*
  75.      * If we're in the middle of an operator AND we had a count before
  76.      * the operator, then that count overrides the current value of
  77.      * Prenum. What this means effectively, is that commands like
  78.      * "3dw" get turned into "d3w" which makes things fall into place
  79.      * pretty neatly.
  80.      */
  81.     if (finish_op) {
  82.         if (opnum != 0)
  83.             Prenum = opnum;
  84.     } else
  85.         opnum = 0;
  86.  
  87.     u_lcheck();    /* clear the "line undo" buffer if we've moved */
  88.  
  89.     switch (c) {
  90.  
  91.     /*
  92.      * Screen positioning commands
  93.      */
  94.     case CTRL('D'):
  95.         CLEAROP;
  96.         if (Prenum)
  97.             P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  98.         scrollup(P(P_SS));
  99.         onedown(P(P_SS));
  100.         updatescreen();
  101.         break;
  102.  
  103.     case CTRL('U'):
  104.         CLEAROP;
  105.         if (Prenum)
  106.             P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  107.         scrolldown(P(P_SS));
  108.         oneup(P(P_SS));
  109.         updatescreen();
  110.         break;
  111.  
  112.     /*
  113.      * This is kind of a hack. If we're moving by one page, the calls
  114.      * to stuffin() do exactly the right thing in terms of leaving
  115.      * some context, and so on. If a count was given, we don't have
  116.      * to worry about these issues.
  117.      */
  118.     case CTRL('F'):
  119.         CLEAROP;
  120.         n = DEFAULT1(Prenum);
  121.         if (n > 1) {
  122.             if ( ! onedown(Rows * n) )
  123.                 beep();
  124.             cursupdate();
  125.         } else {
  126.             screenclear();
  127.             stuffin("Lz\nM");
  128.         }
  129.         break;
  130.  
  131.     case CTRL('B'):
  132.         CLEAROP;
  133.         n = DEFAULT1(Prenum);
  134.         if (n > 1) {
  135.             if ( ! oneup(Rows * n) )
  136.                 beep();
  137.             cursupdate();
  138.         } else {
  139.             screenclear();
  140.             stuffin("Hz-M");
  141.         }
  142.         break;
  143.  
  144.     case CTRL('E'):
  145.         CLEAROP;
  146.         scrollup(DEFAULT1(Prenum));
  147.         updatescreen();
  148.         break;
  149.  
  150.     case CTRL('Y'):
  151.         CLEAROP;
  152.         scrolldown(DEFAULT1(Prenum));
  153.         updatescreen();
  154.         break;
  155.  
  156.     case 'z':
  157.         CLEAROP;
  158.         switch (vgetc()) {
  159.         case NL:        /* put Curschar at top of screen */
  160.         case CR:
  161.             *Topchar = *Curschar;
  162.             Topchar->index = 0;
  163.             updatescreen();
  164.             break;
  165.  
  166.         case '.':        /* put Curschar in middle of screen */
  167.             n = Rows/2;
  168.             goto dozcmd;
  169.  
  170.         case '-':        /* put Curschar at bottom of screen */
  171.             n = Rows-1;
  172.             /* fall through */
  173.  
  174.         dozcmd:
  175.             {
  176.                 register LPTR    *lp = Curschar;
  177.                 register int    l = 0;
  178.  
  179.                 while ((l < n) && (lp != NULL)) {
  180.                     l += plines(lp);
  181.                     *Topchar = *lp;
  182.                     lp = prevline(lp);
  183.                 }
  184.             }
  185.             Topchar->index = 0;
  186.             updatescreen();
  187.             break;
  188.  
  189.         default:
  190.             beep();
  191.         }
  192.         break;
  193.  
  194.     /*
  195.      * Control commands
  196.      */
  197.     case ':':
  198.         CLEAROP;
  199.         if ((s = getcmdln(c)) != NULL)
  200.             docmdln(s);
  201.         break;
  202.  
  203.     case K_HELP:
  204.         CLEAROP;
  205.         if (help()) {
  206.             screenclear();
  207.             updatescreen();
  208.         }
  209.         break;
  210.  
  211.     case CTRL('L'):
  212.         CLEAROP;
  213.         screenclear();
  214.         updatescreen();
  215.         break;
  216.  
  217.  
  218.     case CTRL('O'):            /* ignored */
  219.         /*
  220.          * A command that's ignored can be useful. We use it at
  221.          * times when we want to postpone redraws. By stuffing
  222.          * in a control-o, redraws get suspended until the editor
  223.          * gets back around to processing input.
  224.          */
  225.         break;
  226.  
  227.     case CTRL('G'):
  228.         CLEAROP;
  229.         fileinfo();
  230.         break;
  231.  
  232.     case K_CGRAVE:            /* shorthand command */
  233.         CLEAROP;
  234.         stuffin(":e #\n");
  235.         break;
  236.  
  237.     case 'Z':            /* write, if changed, and exit */
  238.         if (vgetc() != 'Z') {
  239.             beep();
  240.             break;
  241.         }
  242.         doxit();
  243.         break;
  244.  
  245.     /*
  246.      * Macro evaluates true if char 'c' is a valid identifier character
  247.      */
  248. #    define    IDCHAR(c)    (isalpha(c) || isdigit(c) || (c) == '_')
  249.  
  250.     case CTRL(']'):            /* :ta to current identifier */
  251.         CLEAROP;
  252.         {
  253.             char    ch;
  254.             LPTR    save;
  255.  
  256.             save = *Curschar;
  257.             /*
  258.              * First back up to start of identifier. This
  259.              * doesn't match the real vi but I like it a
  260.              * little better and it shouldn't bother anyone.
  261.              */
  262.             ch = gchar(Curschar);
  263.             while (IDCHAR(ch)) {
  264.                 if (!oneleft())
  265.                     break;
  266.                 ch = gchar(Curschar);
  267.             }
  268.             if (!IDCHAR(ch))
  269.                 oneright();
  270.  
  271.             stuffin(":ta ");
  272.             /*
  273.              * Now grab the chars in the identifier
  274.              */
  275.             ch = gchar(Curschar);
  276.             while (IDCHAR(ch)) {
  277.                 stuffin(mkstr(ch));
  278.                 if (!oneright())
  279.                     break;
  280.                 ch = gchar(Curschar);
  281.             }
  282.             stuffin("\n");
  283.  
  284.             *Curschar = save;    /* restore, in case of error */
  285.         }
  286.         break;
  287.  
  288.     /*
  289.      * Character motion commands
  290.      */
  291.     case 'G':
  292.         mtype = MLINE;
  293.         *Curschar = *gotoline(Prenum);
  294.         beginline(TRUE);
  295.         break;
  296.  
  297.     case 'H':
  298.         mtype = MLINE;
  299.         *Curschar = *Topchar;
  300.         for (n = Prenum; n && onedown(1) ;n--)
  301.             ;
  302.         beginline(TRUE);
  303.         break;
  304.  
  305.     case 'M':
  306.         mtype = MLINE;
  307.         *Curschar = *Topchar;
  308.         for (n = 0; n < Rows/2 && onedown(1) ;n++)
  309.             ;
  310.         beginline(TRUE);
  311.         break;
  312.  
  313.     case 'L':
  314.         mtype = MLINE;
  315.         *Curschar = *prevline(Botchar);
  316.         for (n = Prenum; n && oneup(1) ;n--)
  317.             ;
  318.         beginline(TRUE);
  319.         break;
  320.  
  321.     case 'l':
  322.     case K_RARROW:
  323.     case ' ':
  324.         mtype = MCHAR;
  325.         mincl = FALSE;
  326.         n = DEFAULT1(Prenum);
  327.         while (n--) {
  328.             if ( ! oneright() )
  329.                 beep();
  330.         }
  331.         set_want_col = TRUE;
  332.         break;
  333.  
  334.     case 'h':
  335.     case K_LARROW:
  336.     case CTRL('H'):
  337.         mtype = MCHAR;
  338.         mincl = FALSE;
  339.         n = DEFAULT1(Prenum);
  340.         while (n--) {
  341.             if ( ! oneleft() ){
  342.                 beep();
  343.                 break;
  344.             }
  345.         }
  346.         set_want_col = TRUE;
  347.         break;
  348.  
  349.     case '-':
  350.         flag = TRUE;
  351.         /* fall through */
  352.  
  353.     case 'k':
  354.     case K_UARROW:
  355.     case CTRL('P'):
  356.         mtype = MLINE;
  357.         if ( ! oneup(DEFAULT1(Prenum)) )
  358.             beep();
  359.         if (flag)
  360.             beginline(TRUE);
  361.         break;
  362.  
  363.     case '+':
  364.     case CR:
  365.     case NL:
  366.         flag = TRUE;
  367.         /* fall through */
  368.  
  369.     case 'j':
  370.     case K_DARROW:
  371.     case CTRL('N'):
  372.         mtype = MLINE;
  373.         if ( ! onedown(DEFAULT1(Prenum)) )
  374.             beep();
  375.         if (flag)
  376.             beginline(TRUE);
  377.         break;
  378.  
  379.     /*
  380.      * This is a strange motion command that helps make operators
  381.      * more logical. It is actually implemented, but not documented
  382.      * in the real 'vi'. This motion command actually refers to "the
  383.      * current line". Commands like "dd" and "yy" are really an alternate
  384.      * form of "d_" and "y_". It does accept a count, so "d3_" works to
  385.      * delete 3 lines.
  386.      */
  387.     case '_':
  388.     lineop:
  389.         mtype = MLINE;
  390.         onedown(DEFAULT1(Prenum)-1);
  391.         break;
  392.  
  393.     case '|':
  394.         mtype = MCHAR;
  395.         mincl = TRUE;
  396.         beginline(FALSE);
  397.         if (Prenum > 0)
  398.             *Curschar = *coladvance(Curschar, Prenum-1);
  399.         Curswant = Prenum - 1;
  400.         break;
  401.         
  402.     /*
  403.      * Word Motions
  404.      */
  405.  
  406.     case 'B':
  407.         type = 1;
  408.         /* fall through */
  409.  
  410.     case 'b':
  411.         mtype = MCHAR;
  412.         mincl = FALSE;
  413.         set_want_col = TRUE;
  414.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  415.             LPTR    *pos;
  416.  
  417.             if ((pos = bck_word(Curschar, type)) == NULL) {
  418.                 beep();
  419.                 CLEAROP;
  420.                 break;
  421.             } else
  422.                 *Curschar = *pos;
  423.         }
  424.         break;
  425.  
  426.     case 'W':
  427.         type = 1;
  428.         /* fall through */
  429.  
  430.     case 'w':
  431.         /*
  432.          * This is a little strange. To match what the real vi
  433.          * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
  434.          * This seems impolite at first, but it's really more
  435.          * what we mean when we say 'cw'.
  436.          */
  437.         if (operator == CHANGE)
  438.             goto doecmd;
  439.  
  440.         mtype = MCHAR;
  441.         mincl = FALSE;
  442.         set_want_col = TRUE;
  443.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  444.             LPTR    *pos;
  445.  
  446.             if ((pos = fwd_word(Curschar, type)) == NULL) {
  447.                 beep();
  448.                 CLEAROP;
  449.                 break;
  450.             } else
  451.                 *Curschar = *pos;
  452.         }
  453.         break;
  454.  
  455.     case 'E':
  456.         type = 1;
  457.         /* fall through */
  458.  
  459.     case 'e':
  460.     doecmd:
  461.         mtype = MCHAR;
  462.         mincl = TRUE;
  463.         set_want_col = TRUE;
  464.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  465.             LPTR    *pos;
  466.  
  467.             /*
  468.              * The first motion gets special treatment if we're
  469.              * do a 'CHANGE'.
  470.              */
  471.             if (n == DEFAULT1(Prenum))
  472.                 pos = end_word(Curschar,type,operator==CHANGE);
  473.             else
  474.                 pos = end_word(Curschar, type, FALSE);
  475.  
  476.             if (pos == NULL) {
  477.                 beep();
  478.                 CLEAROP;
  479.                 break;
  480.             } else
  481.                 *Curschar = *pos;
  482.         }
  483.         break;
  484.  
  485.     case '$':
  486.         mtype = MCHAR;
  487.         mincl = TRUE;
  488.         while ( oneright() )
  489.             ;
  490.         Curswant = 999;        /* so we stay at the end */
  491.         break;
  492.  
  493.     case '^':
  494.         mtype = MCHAR;
  495.         mincl = FALSE;
  496.         beginline(TRUE);
  497.         break;
  498.  
  499.     case '0':
  500.         mtype = MCHAR;
  501.         mincl = TRUE;
  502.         beginline(FALSE);
  503.         break;
  504.  
  505.     /*
  506.      * Searches of various kinds
  507.      */
  508.     case '?':
  509.     case '/':
  510.         s = getcmdln(c);    /* get the search string */
  511.  
  512.         /*
  513.          * If they backspaced out of the search command,
  514.          * just bag everything.
  515.          */
  516.         if (s == NULL) {
  517.             CLEAROP;
  518.             break;
  519.         }
  520.  
  521.         mtype = MCHAR;
  522.         mincl = FALSE;
  523.         set_want_col = TRUE;
  524.  
  525.         /*
  526.          * If no string given, pass NULL to repeat the prior search.
  527.          * If the search fails, abort any pending operator.
  528.          */
  529.         if (!dosearch(
  530.                 (c == '/') ? FORWARD : BACKWARD,
  531.                 (*s == NUL) ? NULL : s
  532.                  ))
  533.             CLEAROP;
  534.         break;
  535.  
  536.     case 'n':
  537.         mtype = MCHAR;
  538.         mincl = FALSE;
  539.         set_want_col = TRUE;
  540.         if (!repsearch(0))
  541.             CLEAROP;
  542.         break;
  543.  
  544.     case 'N':
  545.         mtype = MCHAR;
  546.         mincl = FALSE;
  547.         set_want_col = TRUE;
  548.         if (!repsearch(1))
  549.             CLEAROP;
  550.         break;
  551.  
  552.     /*
  553.      * Character searches
  554.      */
  555.     case 'T':
  556.         dir = BACKWARD;
  557.         /* fall through */
  558.  
  559.     case 't':
  560.         type = 1;
  561.         goto docsearch;
  562.  
  563.     case 'F':
  564.         dir = BACKWARD;
  565.         /* fall through */
  566.  
  567.     case 'f':
  568.     docsearch:
  569.         mtype = MCHAR;
  570.         mincl = TRUE;
  571.         set_want_col = TRUE;
  572.         if ((nchar = vgetc()) == ESC)    /* search char */
  573.             break;
  574.  
  575.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  576.             if (!searchc(nchar, dir, type)) {
  577.                 CLEAROP;
  578.                 beep();
  579.             }
  580.         }
  581.         break;
  582.  
  583.     case ',':
  584.         flag = 1;
  585.         /* fall through */
  586.  
  587.     case ';':
  588.         mtype = MCHAR;
  589.         mincl = TRUE;
  590.         set_want_col = TRUE;
  591.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  592.             if (!crepsearch(flag)) {
  593.                 CLEAROP;
  594.                 beep();
  595.             }
  596.         }
  597.         break;
  598.  
  599.     case '[':            /* function searches */
  600.         dir = BACKWARD;
  601.         /* fall through */
  602.  
  603.     case ']':
  604.         mtype = MLINE;
  605.         set_want_col = TRUE;
  606.         if (vgetc() != c) {
  607.             beep();
  608.             CLEAROP;
  609.             break;
  610.         }
  611.  
  612.         if (!findfunc(dir)) {
  613.             beep();
  614.             CLEAROP;
  615.         }
  616.         break;
  617.  
  618.     case '%':
  619.         mtype = MCHAR;
  620.         mincl = TRUE;
  621.         {
  622.             LPTR    *pos;
  623.  
  624.             if ((pos = showmatch()) == NULL) {
  625.                 beep();
  626.                 CLEAROP;
  627.             } else {
  628.                 setpcmark();
  629.                 *Curschar = *pos;
  630.                 set_want_col = TRUE;
  631.             }
  632.         }
  633.         break;
  634.         
  635.     /*
  636.      * Edits
  637.      */
  638.     case '.':        /* repeat last change (usually) */
  639.         /*
  640.          * If a delete is in effect, we let '.' help out the same
  641.          * way that '_' helps for some line operations. It's like
  642.          * an 'l', but subtracts one from the count and is inclusive.
  643.          */
  644.         if (operator == DELETE || operator == CHANGE) {
  645.             if (Prenum != 0) {
  646.                 n = DEFAULT1(Prenum) - 1;
  647.                 while (n--)
  648.                     if (! oneright())
  649.                         break;
  650.             }
  651.             mtype = MCHAR;
  652.             mincl = TRUE;
  653.         } else {            /* a normal 'redo' */
  654.             CLEAROP;
  655.             stuffin(Redobuff);
  656.         }
  657.         break;
  658.  
  659.     case 'u':
  660.     case K_UNDO:
  661.         CLEAROP;
  662.         u_undo();
  663.         break;
  664.  
  665.     case 'U':
  666.         CLEAROP;
  667.         u_lundo();
  668.         break;
  669.  
  670.     case 'x':
  671.         CLEAROP;
  672.         if (lineempty())    /* can't do it on a blank line */
  673.             beep();
  674.         if (Prenum)
  675.             stuffnum(Prenum);
  676.         stuffin("d.");
  677.         break;
  678.  
  679.     case 'X':
  680.         CLEAROP;
  681.         if (!oneleft())
  682.             beep();
  683.         else {
  684.             strcpy(Redobuff, "X");
  685.             u_saveline();
  686.             delchar(TRUE);
  687.             updateline();
  688.         }
  689.         break;
  690.  
  691.     case 'r':
  692.         CLEAROP;
  693.         if (lineempty()) {    /* Nothing to replace */
  694.             beep();
  695.             break;
  696.         }
  697.         if ((nchar = vgetc()) == ESC)
  698.             break;
  699.  
  700.         if (
  701. #ifndef MACINTOSH
  702.             (nchar & 0x80) ||
  703. #endif
  704.              nchar == CR || nchar == NL) {
  705.             beep();
  706.             break;
  707.         }
  708.         u_saveline();
  709.  
  710.         /* Change current character. */
  711.         pchar(Curschar, nchar);
  712.  
  713.         /* Save stuff necessary to redo it */
  714.         sprintf(Redobuff, "r%c", nchar);
  715.  
  716.         CHANGED;
  717.         updateline();
  718.         break;
  719.  
  720.     case '~':        /* swap case */
  721.         if (!P(P_TO)) {
  722.             CLEAROP;
  723.             if (lineempty()) {
  724.                 beep();
  725.                 break;
  726.             }
  727.             c = gchar(Curschar);
  728.  
  729.             if (isalpha(c)) {
  730.                 if (islower(c))
  731.                     c = toupper(c);
  732.                 else
  733.                     c = tolower(c);
  734.             }
  735.             u_saveline();
  736.  
  737.             pchar(Curschar, c);    /* Change current character. */
  738.             oneright();
  739.  
  740.             strcpy(Redobuff, "~");
  741.  
  742.             CHANGED;
  743.             updateline();
  744.         }
  745. #ifdef    TILDEOP
  746.         else {
  747.             if (operator == TILDE)        /* handle '~~' */
  748.                 goto lineop;
  749.             if (Prenum != 0)
  750.                 opnum = Prenum;
  751.             startop = *Curschar;
  752.             operator = TILDE;
  753.         }
  754. #endif
  755.  
  756.         break;
  757.  
  758.     case 'J':
  759.         CLEAROP;
  760.  
  761.         u_save(Curschar->linep->prev, Curschar->linep->next->next);
  762.  
  763.         if (!dojoin(TRUE))
  764.             beep();
  765.  
  766.         strcpy(Redobuff, "J");
  767.         updatescreen();
  768.         break;
  769.  
  770.     /*
  771.      * Inserts
  772.      */
  773.     case 'A':
  774.         set_want_col = TRUE;
  775.         while (oneright())
  776.             ;
  777.         /* fall through */
  778.  
  779.     case 'a':
  780.         CLEAROP;
  781.         /* Works just like an 'i'nsert on the next character. */
  782.         if (!lineempty())
  783.             inc(Curschar);
  784.         u_saveline();
  785.         startinsert(mkstr(c), FALSE);
  786.         break;
  787.  
  788.     case 'I':
  789.         beginline(TRUE);
  790.         /* fall through */
  791.  
  792.     case 'i':
  793.     case K_INSERT:
  794.         CLEAROP;
  795.         u_saveline();
  796.         startinsert(mkstr(c), FALSE);
  797.         break;
  798.  
  799.     case 'o':
  800.         CLEAROP;
  801.         u_save(Curschar->linep, Curschar->linep->next);
  802.         opencmd(FORWARD, TRUE);
  803.         startinsert("o", TRUE);
  804.         break;
  805.  
  806.     case 'O':
  807.         CLEAROP;
  808.         u_save(Curschar->linep->prev, Curschar->linep);
  809.         opencmd(BACKWARD, TRUE);
  810.         startinsert("O", TRUE);
  811.         break;
  812.  
  813.     case 'R':
  814.         CLEAROP;
  815.         u_saveline();
  816.         startinsert("R", FALSE);
  817.         break;
  818.  
  819.     /*
  820.      * Operators
  821.      */
  822.     case 'd':
  823.         if (operator == DELETE)        /* handle 'dd' */
  824.             goto lineop;
  825.         if (Prenum != 0)
  826.             opnum = Prenum;
  827.         startop = *Curschar;
  828.         operator = DELETE;
  829.         break;
  830.  
  831.     case 'c':
  832.         if (operator == CHANGE)        /* handle 'cc' */
  833.             goto lineop;
  834.         if (Prenum != 0)
  835.             opnum = Prenum;
  836.         startop = *Curschar;
  837.         operator = CHANGE;
  838.         break;
  839.  
  840.     case 'y':
  841.         if (operator == YANK)        /* handle 'yy' */
  842.             goto lineop;
  843.         if (Prenum != 0)
  844.             opnum = Prenum;
  845.         startop = *Curschar;
  846.         operator = YANK;
  847.         break;
  848.  
  849.     case '>':
  850.         if (operator == RSHIFT)        /* handle >> */
  851.             goto lineop;
  852.         if (Prenum != 0)
  853.             opnum = Prenum;
  854.         startop = *Curschar;
  855.         operator = RSHIFT;
  856.         break;
  857.  
  858.     case '<':
  859.         if (operator == LSHIFT)        /* handle << */
  860.             goto lineop;
  861.         if (Prenum != 0)
  862.             opnum = Prenum;
  863.         startop = *Curschar;    /* save current position */
  864.         operator = LSHIFT;
  865.         break;
  866.  
  867.     case '!':
  868.         if (operator == FILTER)        /* handle '!!' */
  869.             goto lineop;
  870.         if (Prenum != 0)
  871.             opnum = Prenum;
  872.         startop = *Curschar;
  873.         operator = FILTER;
  874.         break;
  875.  
  876.     case 'p':
  877.         doput(FORWARD);
  878.         break;
  879.  
  880.     case 'P':
  881.         doput(BACKWARD);
  882.         break;
  883.  
  884.     /*
  885.      * Abbreviations
  886.      */
  887.     case 'D':
  888.         stuffin("d$");
  889.         break;
  890.  
  891.     case 'Y':
  892.         if (Prenum)
  893.             stuffnum(Prenum);
  894.         stuffin("yy");
  895.         break;
  896.  
  897.     case 'C':
  898.         stuffin("c$");
  899.         break;
  900.  
  901.     case 's':                /* substitute characters */
  902.         if (Prenum)
  903.             stuffnum(Prenum);
  904.         stuffin("c.");
  905.         break;
  906.  
  907.     /*
  908.      * Marks
  909.      */
  910.     case 'm':
  911.         CLEAROP;
  912.         if (!setmark(vgetc()))
  913.             beep();
  914.         break;
  915.  
  916.     case '\'':
  917.         flag = TRUE;
  918.         /* fall through */
  919.  
  920.     case '`':
  921.         {
  922.             LPTR    mtmp, *mark = getmark(vgetc());
  923.  
  924.             if (mark == NULL) {
  925.                 beep();
  926.                 CLEAROP;
  927.             } else {
  928.                 mtmp = *mark;
  929.                 setpcmark();
  930.                 *Curschar = mtmp;
  931.                 if (flag)
  932.                     beginline(TRUE);
  933.             }
  934.             mtype = flag ? MLINE : MCHAR;
  935.             mincl = TRUE;        /* ignored if not MCHAR */
  936.             set_want_col = TRUE;
  937.         }
  938.         break;
  939.  
  940.     default:
  941.         CLEAROP;
  942.         beep();
  943.         break;
  944.     }
  945.  
  946.     /*
  947.      * If an operation is pending, handle it...
  948.      */
  949.     if (finish_op) {        /* we just finished an operator */
  950.         if (operator == NOP)    /* ... but it was cancelled */
  951.             return;
  952.  
  953.         switch (operator) {
  954.  
  955.         case LSHIFT:
  956.         case RSHIFT:
  957.             doshift(operator, c, nchar, Prenum);
  958.             break;
  959.  
  960.         case DELETE:
  961.             dodelete(c, nchar, Prenum);
  962.             break;
  963.  
  964.         case YANK:
  965.             (void) doyank();    /* no redo on yank... */
  966.             break;
  967.  
  968.         case CHANGE:
  969.             dochange(c, nchar, Prenum);
  970.             break;
  971.  
  972.         case FILTER:
  973.             dofilter(c, nchar, Prenum);
  974.             break;
  975.  
  976. #ifdef    TILDEOP
  977.         case TILDE:
  978.             dotilde(c, nchar, Prenum);
  979.             break;
  980. #endif
  981.  
  982.         default:
  983.             beep();
  984.         }
  985.         operator = NOP;
  986.     }
  987. }
  988.